﻿using System;
using System.Text;

namespace Adaptive.Foundation.Tokenizers
{
    /// <summary>
    /// Provides a tokenizer for character-based simple token list creation.
    /// </summary>
    /// <remarks>
    /// 
    /// </remarks>
    public class CharacterTokenizer : AiObjectBase 
    {
        /*--------------------------------------------------------------------------------
         * Public Events
         *------------------------------------------------------------------------------*/
        #region Public Events
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Table containing the token definitions.
        /// </summary>
        private CharacterTypeTable _tokenTable;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public CharacterTokenizer()
            : base()
        {
            _tokenTable = new CharacterTypeTable();
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="CharacterTokenizer"/> class.
        /// </summary>
        /// <param name="characterTokens">
        /// The <see cref="CharacterTypeTable"/> table instance to use when performing
        /// tokenizing operations.
        /// </param>
        public CharacterTokenizer(CharacterTypeTable characterTokens)
        {
            _tokenTable = characterTokens;
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                if (_tokenTable != null)
                    _tokenTable.Dispose();
            }

            _tokenTable = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets the reference to the token table used for processing.
        /// </summary>
        /// <value>
        /// A <see cref="CharacterTypeTable"/> instance.
        /// </value>
        public CharacterTypeTable Table
        {
            get
            {
                return _tokenTable;
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods
        /// <summary>
        /// Locates the position of the next recognized token value.
        /// </summary>
        /// <param name="startIndex">
        /// The ordinal indexe at which to begin examining the provided string.
        /// </param>
        /// <param name="originalData">
        /// The string to examine.
        /// </param>
        /// <returns>
        /// The character index of the next character token.
        /// </returns>
        protected virtual int IsolateNextToken(int startIndex, string originalData)
        {
            int returnValue = -1;                       //Return value.
            int count = 0;                              //Iteration counter.
            int length = 0;                             //Iteration limit.

            count = startIndex;
            length = originalData.Length;
            do
            {
                if (_tokenTable.IsToken(originalData[count]))
                {
                    returnValue = count;
                }
                else
                {
                    count++;
                }
            } while ((returnValue == -1) && (count < length));

            return returnValue;
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Tokenizes the provided string data.
        /// </summary>
        /// <param name="originalData">
        /// The data to be tokenized.
        /// </param>
        /// <returns>
        /// A <see cref="RawTokenCollection"/> containing the tokenized data.
        /// </returns>
        public RawTokenCollection Tokenize(string originalData)
        {
            RawTokenCollection returnValue = null;              // Return value.
            RawToken newToken = null;                           // New token instance.
            int currentPosition = -1;                           // Current string index position.
            int nextTokenPosition = -1;                         // Position of next known token type.
            int length = 0;                                     // Iteration limit.
            StringBuilder builder = null;                       // Data container.

            returnValue = new RawTokenCollection();
            builder = new StringBuilder();

            if (!string.IsNullOrEmpty(originalData))
            {
                currentPosition = 0;
                length = originalData.Length;
                do
                {
                    nextTokenPosition = IsolateNextToken(currentPosition, originalData);

                    if (nextTokenPosition == -1)
                    {
                        //Store data token if any data remains.fs
                        if (currentPosition < originalData.Length - 1)
                        {
                            newToken = new RawToken();
                            newToken.TokenType = CharacterTokenTypes.Unspecified;
                            newToken.RawDataLocation = currentPosition;
                            newToken.TokenValue = originalData.Substring(currentPosition, originalData.Length - currentPosition);
                            returnValue.Add(newToken);
                            currentPosition = originalData.Length;
                        }
                    }
                    else
                    {
                        if (nextTokenPosition > currentPosition)
                        {
                            //Store data token.
                            newToken = new RawToken();
                            newToken.TokenType = CharacterTokenTypes.Unspecified;
                            newToken.RawDataLocation = currentPosition;
                            newToken.TokenValue = originalData.Substring(currentPosition, nextTokenPosition - currentPosition);
                            returnValue.Add(newToken);
                        }

                        //Store character token.
                        newToken = new RawToken();
                        newToken.TokenValue = originalData.Substring(nextTokenPosition, 1);
                        newToken.TokenType = _tokenTable.GetTypeForCharacter(newToken.TokenValue[0]);
                        newToken.RawDataLocation = nextTokenPosition;
                        returnValue.Add(newToken);

                        currentPosition = nextTokenPosition;
                    }
                    currentPosition++;
                } while (currentPosition < length);

            }

            return returnValue;
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        #endregion
    }
}